
package chapter7;

import javax.vecmath.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import java.applet.*;
import com.sun.j3d.utils.applet.MainFrame;

public class Ex9 extends Applet {
  public static void main(String[] args) {
    new MainFrame(new Ex9(), 640, 480);
  }
  
  public void init() {
    // create canvas
    GraphicsConfiguration gc = SimpleUniverse.getPreferredConfiguration();
    Canvas3D cv = new Canvas3D(gc);
    setLayout(new BorderLayout());
    add(cv, BorderLayout.CENTER);
    BranchGroup bg = createSceneGraph();
    bg.compile();
    SimpleUniverse su = new SimpleUniverse(cv);
    su.getViewingPlatform().setNominalViewingTransform();
    su.addBranchGraph(bg);
  }
  
  private BranchGroup createSceneGraph() {
    BranchGroup root = new BranchGroup();
    TransformGroup spin = new TransformGroup();
    spin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    root.addChild(spin);
    
    Transform3D tr = new Transform3D();
    tr.setScale(0.5);
    tr.setRotation(new AxisAngle4d(1, 0, 0, Math.PI/6));
    TransformGroup tg = new TransformGroup(tr);
    spin.addChild(tg);
    
    //object
    GeneralPath path = new GeneralPath();
    path.moveTo(0.1f,-0.5f);
    path.quadTo(0.5f,1,0.8f,0.5f);
    path.lineTo(1,0.2f);
    Shape3D shape = new RotatedShape(path);
    Appearance ap = new Appearance();
    ap.setMaterial(new Material());
    PolygonAttributes pa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
    PolygonAttributes.CULL_NONE,0);
    pa.setBackFaceNormalFlip(true);
    ap.setPolygonAttributes(pa);
    shape.setAppearance(ap);
    tg.addChild(shape);
    
    Alpha alpha = new Alpha(-1, 8000);
    RotationInterpolator rotator = new RotationInterpolator(alpha, spin);
    BoundingSphere bounds = new BoundingSphere();
    rotator.setSchedulingBounds(bounds);
    spin.addChild(rotator);
    
    //background and lights
    Background background = new Background(1.0f, 1.0f, 1.0f);
    background.setApplicationBounds(bounds);
    root.addChild(background);
    AmbientLight light = new AmbientLight(true, new Color3f(Color.blue));
    light.setInfluencingBounds(bounds);
//    root.addChild(light);
    PointLight ptlight = new PointLight(new Color3f(Color.white),
    new Point3f(3f,3f,3f), new Point3f(1f,0f,0f));
    ptlight.setInfluencingBounds(bounds);
    root.addChild(ptlight);
    return root;
  }
} 

class RotatedShape extends Shape3D {
  public RotatedShape(Shape curve) {
    PathIterator iter = curve.getPathIterator(new AffineTransform());
    Vector ptsList = new Vector();
    float[] seg = new float[6];
    float x = 0, y = 0;
    float x0 = 0, y0 = 0;
    while (!iter.isDone()) {
      int segType = iter.currentSegment(seg);
      switch (segType) {
        case PathIterator.SEG_MOVETO:
          x = x0 = seg[0];
          y = y0 = seg[1];
          ptsList.add(new Point3f(x,y,0));
          break;
        case PathIterator.SEG_LINETO:
          x = seg[0];
          y = seg[1];
          ptsList.add(new Point3f(x,y,0));
          break;
        case PathIterator.SEG_QUADTO:
          for (int i = 1; i < 10; i++) {
            float t = (float)i/10f;
            float xi = (1-t)*(1-t)*x + 2*t*(1-t)*seg[0] + t*t*seg[2];
            float yi = (1-t)*(1-t)*y + 2*t*(1-t)*seg[1] + t*t*seg[3];
            ptsList.add(new Point3f(xi,yi,0));
          }
          x = seg[2];
          y = seg[3];
          ptsList.add(new Point3f(x,y,0));
          break;
        case PathIterator.SEG_CUBICTO:
          for (int i = 1; i < 20; i++) {
            float t = (float)i/20f;
            float xi = (1-t)*(1-t)*(1-t)*x + 3*t*(1-t)*(1-t)*seg[0] +
            3*t*t*(1-t)*seg[2] + t*t*t*seg[4];
            float yi = (1-t)*(1-t)*(1-t)*y + 3*t*(1-t)*(1-t)*seg[1] +
            3*t*t*(1-t)*seg[3] + t*t*t*seg[5];
            ptsList.add(new Point3f(xi,yi,0));
          }
          x = seg[2];
          y = seg[3];
          ptsList.add(new Point3f(x,y,0));
          break;
        case PathIterator.SEG_CLOSE:
          x = x0;
          y = y0;
          ptsList.add(new Point3f(x,y,0));
          break;
      }
      iter.next();
    }
    int m = 60;
    int n = ptsList.size();
    IndexedQuadArray qa = new IndexedQuadArray(2*(m+1)*n,
    IndexedQuadArray.COORDINATES, 4*m*(n-1));
    Transform3D trans = new Transform3D();
    trans.rotY(2*Math.PI/m);
    for (int j = 0; j <= m; j++) {
      for (int i = 0; i < n; i++) {
        Point3f pt = (Point3f)ptsList.get(i);
        qa.setCoordinate(j*n+i, pt);
        trans.transform(pt);
      }
    }
    int quadIndex = 0;
    for (int j = 0; j < m; j++) {
      for (int i = 0; i < n-1; i++) {
        qa.setCoordinateIndex(quadIndex++, j*n+i);
        qa.setCoordinateIndex(quadIndex++, j*n+i+1);
        qa.setCoordinateIndex(quadIndex++, (j+1)*n+i+1);
        qa.setCoordinateIndex(quadIndex++, (j+1)*n+i);
      }
    }
    GeometryInfo gi = new GeometryInfo(qa);
    NormalGenerator ng = new NormalGenerator();
    ng.generateNormals(gi);
    this.setGeometry(gi.getGeometryArray());
  }
}
